package sample;

import java.applet.Applet;
import java.awt.Button;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import triangulation.DCEL;
import triangulation.Polygon;
import triangulation.Triangle;
import triangulation.Triangulation;
import dual.DualGraph;
import dual.Guard;

@SuppressWarnings("serial")
public class ArtGalleryMain extends Applet implements MouseListener, MouseMotionListener
{
	public ArtGallery artGallerySample;
	private Polygon polygon;

	/**
	 * Holds the number of mouse clicks. Resets to 0 after each 2 clicks.
	 */
	private int mouseClickCounter;

	/**
	 * Index of the selected vertex.
	 */
	private int selectedVertexIndex;

	/**
	 * This button should be clicked on in order to create a vertex
	 */
	private Button createVertex;

	/**
	 * This button should be clicked on, in order to run the algorithm.
	 */
	private Button triangulateButton;

	/**
	 * It is clicked in order to move a vertex.
	 */
	private Button moveVertex;

	/**
	 * This button should be clicked on, in order to create the dual graph.
	 */
	private Button createDual;

	/**
	 * This button is clicked to see the guards.
	 */
	private Button showGuards;

	/**
	 * This button cleans the edges and the vertices / canvas.
	 */
	private Button clear;

	/**
	 * This button is clicked if the user wants to see which polygon is guarded by which guard.
	 */
	private Button showRegions;

	/**
	 * The buttons etc are added onto this canvas
	 */
	public JPanel canvas;

	private boolean addVertexFlag;

	/**
	 * Indicates if the triangulation algorithm has run.
	 */
	private boolean isTriangulated;
	private boolean moveVertexFlag;
	private boolean triangulationFlag;
	private boolean dualFlag;
	private boolean showGuardsFlag;
	private boolean showRegionsFlag;
	private boolean polygonFlag;

	public Polygon triangulatedPolygon;
	/**
	 * Stores the triangles if the triangulation algorithm has run.
	 */
	public ArrayList<Triangle> triangleList;

	/**
	 * Stores the dual graph if the create dual button has been clicked on.
	 */
	public DualGraph dGraph;

	private Random numGen;

	public ArtGalleryMain()
	{
		polygon = new Polygon();
		triangleList = new ArrayList<Triangle>();
		numGen = new Random();
	}

	private void performTriangulation()
	{
		polygon.generateDcelOfPolygon();
		triangulatedPolygon = Triangulation.triangulate(polygon);
		isTriangulated = true;

		triangleList = Triangulation.triangleList;

	}

	/**
	 * If it is the first click on a vertex, select it. else deselect it.
	 * 
	 * @param index
	 */
	private void handleIntersectionWithVertex(int index)
	{
		mouseClickCounter++;

		if (mouseClickCounter == 1 && moveVertexFlag)
		{
			deselectVertexIfAny();
			selectedVertexIndex = index;
			polygon.getVertices().get(selectedVertexIndex).setSelected(true);
		}
		else if (mouseClickCounter == 2)
		{
			if (selectedVertexIndex == index)
			{
				deselectVertexIfAny();
			}

			mouseClickCounter = 0;
		}
	}

	/**
	 * Detects intersection between the given coordinates (usually the mouse click) and vertices. If an intersection is found, index of the
	 * intersected vertex is returned. If no intersection is found, -1 is returned.
	 * 
	 * @param x
	 *            - x coordinate of the point
	 * @param y
	 *            - y coordinate of the point
	 * @return index of the vertex.
	 */
	public int vertexIntersection(int x, int y)
	{
		int intersectedVertexIndex = -1;
		for (int i = 0; i < polygon.getVertices().size(); i++)
		{
			if (polygon.getVertices().get(i).isIntersected(new Vertex(x, y)))
			{
				intersectedVertexIndex = i;
				break;
			}
		}

		return intersectedVertexIndex;
	}

	private void deselectVertexIfAny()
	{
		if (selectedVertexIndex >= 0)
		{
			polygon.getVertices().get(selectedVertexIndex).setSelected(false);
			selectedVertexIndex = -1;
		}
	}

	public void init()
	{
		mouseClickCounter = 0;
		selectedVertexIndex = -1;
		addVertexFlag = true;
		moveVertexFlag = false;
		isTriangulated = false;
		triangulationFlag = false;
		dualFlag = false;
		showGuardsFlag = false;
		showRegionsFlag = false;
		polygonFlag = true;

		setBackground(Color.WHITE);

		// set the applet's properties
		canvas = new JPanel();
		this.setSize(700, 700);
		this.setPreferredSize(new Dimension(700, 700));

		// set the buttons orientation
		createVertex = new Button("Add Vertex");
		moveVertex = new Button("Move Vertex");
		clear = new Button("Clear");
		triangulateButton = new Button("Triangulate");
		createDual = new Button("Create Dual");
		showGuards = new Button("Show Guards");
		showRegions = new Button("Show Guarded Regions");

		canvas.add(createVertex);
		canvas.add(moveVertex);
		canvas.add(clear);
		canvas.add(triangulateButton);
		canvas.add(createDual);
		canvas.add(showGuards);
		canvas.add(showRegions);
		this.add(canvas);

		createVertex.addMouseListener(this);
		moveVertex.addMouseListener(this);
		clear.addMouseListener(this);
		triangulateButton.addMouseListener(this);
		createDual.addMouseListener(this);
		showGuards.addMouseListener(this);
		showRegions.addMouseListener(this);

		addMouseListener(this);
		addMouseMotionListener(this);
	}

	public void paint(Graphics g)
	{
		g.setColor(Color.BLACK);

		if (addVertexFlag)
			g.drawString("Add vertex mode is on.", 10, 10);
		else if (polygon.getVertices().size() > 0 && moveVertexFlag)
			g.drawString("You can move a vertex now.", 10, 10);

		if (!triangulationFlag && polygonFlag)
		{
			polygon.draw(g, Color.BLACK);
			polygon.drawVertices(g, Color.BLACK);
		}
		if (artGallerySample != null && artGallerySample.guards.size() > 0 && showRegionsFlag)
		{
			paintGuardedRegions(g);
		}
		if (isTriangulated && triangulationFlag)
		{
			Triangulation.draw(g);
		}
		if (dGraph != null && dualFlag)
		{
			dGraph.draw(g);
		}
		if (showGuardsFlag && dGraph != null)
		{
			if (artGallerySample != null) for (Guard gd : artGallerySample.guards)
			{
				gd.draw(g, Color.RED);
			}
		}

	}

	private void paintGuardedRegions(Graphics g)
	{
		Color[] colorArray = new Color[artGallerySample.guards.size()];
		for (int i = 0; i < artGallerySample.guards.size(); i++)
		{
			colorArray[i] = getRandomColor();
			artGallerySample.guards.get(i).guardedPolygon.paintPolygon(g, colorArray[i]);
		}
		for (int i = 0; i < artGallerySample.guards.size(); i++)
		{
			artGallerySample.guards.get(i).draw(g, colorArray[i]);
		}

	}

	private Color getRandomColor()
	{
		return new Color(numGen.nextInt(256), numGen.nextInt(256), numGen.nextInt(256));
	}

	@Override
	public void mouseDragged(MouseEvent e)
	{
		if (selectedVertexIndex >= 0 && moveVertexFlag)
		{
			// TODO make an intersection test. we should not be able to move a
			// vertex if it intersects with the others.
			polygon.getVertices().get(selectedVertexIndex).setCoordinates(e.getX(), e.getY());

			polygon.dcel = new DCEL();
			isTriangulated = false;
			repaint();

		}
		e.consume();
	}

	@Override
	public void mouseClicked(MouseEvent e)
	{
		if (SwingUtilities.isLeftMouseButton(e))
		{
			if (e.getSource() == createVertex && !polygon.isClosed) // add vertex button
			{
				addVertexFlag = !addVertexFlag;
				mouseClickCounter = 0;
				moveVertexFlag = false;
				deselectVertexIfAny();
			}
			else if (e.getSource() == moveVertex) // move vertex button
			{
				moveVertexFlag = true;
				addVertexFlag = false;
			}
			else if (e.getSource() == clear) // clear button
				handleClear();

			else if (e.getSource() == triangulateButton) // triangulation button
				handleTriangulation();

			else if (e.getSource() == createDual) // dual creation button
				handleDualCreation();

			else if (e.getSource() == showGuards)
			{
				if (dGraph == null)
					JOptionPane.showMessageDialog(canvas, "Create dual graph first.");
				else
					showGuardsFlag = !showGuardsFlag;
			}
			else if (e.getSource() == showRegions)
			{
				if (dGraph == null)
					JOptionPane.showMessageDialog(canvas, "Create dual graph first.");
				else
				{
					showGuardsFlag = false;
					polygonFlag = !polygonFlag;
					showRegionsFlag = !showRegionsFlag;
				}
			}
			else
				handleClickOnCanvas(e);
		}
		else
			handleRightClick(e);

		e.consume();
		repaint();
	}

	private void handleRightClick(MouseEvent e)
	{
		Vertex vs = polygon.getVertices().get(polygon.getVertices().size() - 1);
		Vertex vt = polygon.getVertices().get(0);

		if (polygon.getVertices().size() > 2
				&& !Triangulation.doesDiagonalIntersectPolygon(new Edge(vs, vt), polygon.getVertices()))
		{
			polygon.connectVertices(polygon.getVertices().get(polygon.getVertices().size() - 1), polygon.getVertices()
					.get(0));
			polygon.isClosed = true;
			addVertexFlag = false;
		}
	}

	private void handleClickOnCanvas(MouseEvent e)
	{
		int intersectionIndex = vertexIntersection(e.getX(), e.getY());

		// Intersection with vertex is detected
		if (intersectionIndex > -1)
		{
			handleIntersectionWithVertex(intersectionIndex);
		}
		else if (intersectionIndex == -1)
		{
			// if no intersection with a vertex,add new vertex and
			// create the edge if there is no intersection with the
			// previous edges.
			if (addVertexFlag)
			{
				mouseClickCounter = 0;
				polygon.addVertex(new Vertex(e.getX(), e.getY()), polygon.getVertices().size());

				if (polygon.getVertices().size() > 1)
				{
					Vertex vs = polygon.getVertices().get(polygon.getVertices().size() - 1);
					Vertex vt = polygon.getVertices().get(polygon.getVertices().size() - 2);

					// if edge does not intersect with any other, add
					// it. else, do not add it, plus remove the vertex
					// that is already added.
					if (!Triangulation.doesDiagonalIntersectPolygon(new Edge(vs, vt), polygon.getVertices()))
						polygon.connectVertices(vs, vt);
					else
						polygon.getVertices().remove(polygon.getVertices().size() - 1);
				}
			}
			deselectVertexIfAny();
		}

	}

	private void handleClear()
	{
		deselectVertexIfAny();
		polygon.clearAdjList();
		polygon.clearVertices();
		polygon.dcel = new DCEL();
		polygon.isClosed = false;
		triangleList.clear();
		isTriangulated = false;
		triangulationFlag = false;
		dualFlag = false;
		moveVertexFlag = false;
		addVertexFlag = true;
		showGuardsFlag = false;
		showRegionsFlag = false;
		polygonFlag = true;
		artGallerySample = null;
		dGraph = null;
	}

	private void handleTriangulation()
	{
		if (polygon.isClosed)
		{
			if (!isTriangulated)
			{
				if (polygon.getVertices().size() < 3)
					JOptionPane.showMessageDialog(canvas, "Create a polygon.");
				else if (!polygon.isClosed)
					JOptionPane.showMessageDialog(canvas, "Close the polygon.(right click)");
				else if (polygon.isClosed) performTriangulation();
				dGraph = null;
				deselectVertexIfAny();
			}
			triangulationFlag = !triangulationFlag;
		}
	}

	private void handleDualCreation()
	{
		dualFlag = !dualFlag;
		if (isTriangulated)
		{
			if (dGraph == null)
			{
				dGraph = new DualGraph(triangleList);
				artGallerySample = new ArtGallery(dGraph.dVertexList);
			}
		}
		else
			JOptionPane.showMessageDialog(canvas, "Perform triangulation first.");
		deselectVertexIfAny();
	}

	@Override
	public void mouseMoved(MouseEvent arg0)
	{

	}

	@Override
	public void mouseEntered(MouseEvent arg0)
	{

	}

	@Override
	public void mouseExited(MouseEvent arg0)
	{

	}

	@Override
	public void mousePressed(MouseEvent arg0)
	{

	}

	@Override
	public void mouseReleased(MouseEvent arg0)
	{

	}

}
